package com.z.redis;

import com.z.redis.aof.Aof;
import com.z.redis.channel.DefaultChannelSelectStrategy;
import com.z.redis.channel.LocalChannelOption;
import com.z.redis.channel.single.NettySingleSelectChannelOption;
import com.z.redis.util.PropertiesUtil;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.util.concurrent.EventExecutorGroup;
import org.apache.log4j.Logger;

import java.net.InetSocketAddress;

public class MyRedisServer implements RedisServer{
    private static final Logger LOGGER = Logger.getLogger(MyRedisServer.class); //日志
    private final RedisCore redisCore = new RedisCoreImpl();//Redis核心功能
    private final ServerBootstrap serverBootstrap = new ServerBootstrap();//Netty服务端启动类
    private final EventExecutorGroup redisSingleEventExecutor;//Netty线程组（单线程）
    private final LocalChannelOption channelOption;//通道配置选项
    private Aof aof;

    //默认构造 创建单线程事件循环组
    public MyRedisServer(){
        channelOption = new DefaultChannelSelectStrategy().select();
        this.redisSingleEventExecutor = new NioEventLoopGroup(1);
    }
    public MyRedisServer(LocalChannelOption channelOption){
        this.channelOption = channelOption;
        this.redisSingleEventExecutor = new NioEventLoopGroup(1);
    }
    public static void main(String[] args) {
        // 使用Netty单线程通道配置启动服务器 默认使用
        new MyRedisServer(new NettySingleSelectChannelOption()).start();
//        new MyRedisServer(); // 根据具体的操作系统选择Netty传输方式
    }
    @Override
    public void start(){
        if(PropertiesUtil.getAppendOnly()){
            aof = new Aof(this.redisCore);
        }
        start0();
    }
    @Override
    public void close(){
        try{
            channelOption.boss().shutdownGracefully();//停止连接
            channelOption.workers().shutdownGracefully();//停止工作
            redisSingleEventExecutor.shutdownGracefully();//停止单线程所有任务
        }catch(Exception e){
            LOGGER.warn("Exception! ",e);
        }
    }
    //Redis服务端的网络层核心，负责启动服务、协议解析、线程调度与业务处理
    public void start0(){
        serverBootstrap.group(channelOption.boss(),channelOption.workers())
                .channel(channelOption.getChannelClass())
                .handler(new LoggingHandler(LogLevel.INFO))//记录服务端Channel的生命周期事件（如绑定、连接建立），日志级别为 INFO
                .option(ChannelOption.SO_BACKLOG, 1024)//操作系统等待连接队列的最大长度，防止高并发时连接丢失（默认128）
                .option(ChannelOption.SO_REUSEADDR, true)//允许端口快速重用
                .childOption(ChannelOption.SO_KEEPALIVE, true)//TCP连接保持活跃，如果一段时间内连接没有数据通信时，TCP会自动发送一个活动探测数据报文
                .option(ChannelOption.SO_KEEPALIVE, PropertiesUtil.getTcpKeepAlive())//动态启动TCP心跳检测，关闭无效连接
                .localAddress(new InetSocketAddress(PropertiesUtil.getNodeAddress(), PropertiesUtil.getNodePort()))//指定监听的IP和端口
                .childHandler(new ChannelInitializer<SocketChannel>(){//配置子Channel处理器链
                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        ChannelPipeline channelPipeline = socketChannel.pipeline();//链式结构的处理器容器
                        channelPipeline.addLast(
                              new ResponseEncoder(),//入站 将Redis响应对象序列化成字节流（RESP）
                              new CommandDecoder(aof)//出站 解析客户端请求字节流为Redis命令对象
//                                /*心跳,管理长连接 20秒无活动断开连接*/
//                                new IdleStateHandler(0, 0, 20)
                        );
                        channelPipeline.addLast(redisSingleEventExecutor,new CommandHandler(redisCore)) ;//业务处理器
                    }
                });
        try{
            ChannelFuture sync = serverBootstrap.bind().sync();//绑定端口，启动服务
            LOGGER.info(sync.channel().localAddress().toString());
        }catch (InterruptedException e){
            LOGGER.warn("Exception! ",e);
            throw new RuntimeException(e);
        }
    }

    @Override
    public RedisCore getRedisCore()
    {
        return redisCore;
    }
}